package com.kerpow.KSheet.character;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Stack;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import android.database.Cursor;
import android.util.Log;

import com.kerpow.KSheet.character.CharacterAdapter.PowerSortMethod;
import com.kerpow.KSheet.character.Stat.StatType;

public class Parser extends DefaultHandler  {
	public static final String PATH = "path";
	private String _path;
	
	private Stack<String> _tagPath;
		
	private CharacterAdapter _db;
	private long _characterID;
	
	private Power _currentPower;
	private Stat _currentStat;
	
	private double _currentBytes;
	private double _totalBytes;
	private int _lastPublishedPercent;
	
	private HashMap<String,Stat> _statFinder;
		
	private MySteps _step;
	private enum MySteps{
		start,
		details,
		statblock,
		ruleselements,
		powers,
		end
	}
	private MyPowerSpecifics _powerAttribute;
	private boolean _powerHaveWeaponData;
	private enum MyPowerSpecifics{
		NotLooking,
		Usage,
		Action
	}

	private OnProgressPublishHandler _OnProgressPublishHandler;
	public interface OnProgressPublishHandler{
		public void onProgressPublish(int percent);
	}
	
	public void setOnProgressPublishHandler(OnProgressPublishHandler handler){
		_OnProgressPublishHandler = handler;
	}
	
	public void loadCharacterSheet(CharacterAdapter db, long characterID, String path){
		_characterID = characterID;
		_tagPath = new Stack<String>();
		_db = db;
		_path = path;

		parseIntoDB();
		
		setWebURLs();
	}

	private void setWebURLs(){
		//for each power on character
		Cursor c = _db.fetchPowers(_characterID, PowerSortMethod.Action);
		if(c.moveToFirst()){
			int nameIndex = c.getColumnIndex(CharacterAdapter.FIELD_POWERS_NAME);
			int idIndex = c.getColumnIndex(CharacterAdapter.FIELD_POWERS_ID);
			do{
				//grab the power stat, who's value is the url
				Stat s = _db.getStat(_characterID,  c.getString(nameIndex), StatType.powerurl);
				
				_db.setPowerWebURL(c.getInt(idIndex), s.Value);
				//Log.i("kerpow","URL set " + c.getString(nameIndex) + " = " + s.Value);
			}while(c.moveToNext());
		}
		c.close();
		//put that url into the power
	}
	

	
	private void parseIntoDB(){
		SetupStatsToFind();
		
		//parse this shit
		_step = MySteps.start;
				
        try {

            //load a few custom stats
            _db.addStat(_characterID, new Stat("Current Hit Points","0", StatType.health));
            _db.addStat(_characterID, new Stat("Current Healing Surges","0", StatType.health));
            _db.addStat(_characterID, new Stat("Action Points","0", StatType.other));
            _db.addStat(_characterID, new Stat("Temporary Hit Points","0", StatType.health));
            _db.addStat(_characterID, new Stat("Current Power Points","0", StatType.other));
            
            /* Create a URL we want to load some xml-data from. */

            /* Get a SAXParser from the SAXPArserFactory. */
            SAXParserFactory spf = SAXParserFactory.newInstance();
            SAXParser sp = spf.newSAXParser();

            /* Get the XMLReader of the SAXParser we created. */
            XMLReader xr = sp.getXMLReader();
            /* Create a new ContentHandler and apply it to the XML-Reader*/
            
            //ExampleHandler myExampleHandler = new ExampleHandler();
            xr.setContentHandler(this);
           
            /* Parse the xml-data from our URL. */
            File file = new File(_path);
            _totalBytes = file.length();
            _currentBytes = 0;
            MyFileReader fr = new MyFileReader(file);
            
            xr.parse(new InputSource(fr));
            /* Parsing has finished. */

            /* Our ExampleHandler now provides the parsed data to us. */
            //ParsedExampleDataSet parsedExampleDataSet = myExampleHandler.getParsedData();

            /* Set the result to be displayed in our GUI. */
            //tv.setText(parsedExampleDataSet.toString());
            
	    } catch (Exception e) {
	    	Log.e("kerpow","SAX: " + e.toString());
	    }
	}
	
	private void SetupStatsToFind(){
		
		_statFinder = new HashMap<String, Stat>();
		_statFinder.put("name", new Stat("Name","",StatType.detail));
		_statFinder.put("Level", new Stat("Level","",StatType.detail));
		
		_statFinder.put("Armor Class", new Stat("Armor Class","",StatType.defense));
		_statFinder.put("Fortitude", new Stat("Fortitude","",StatType.defense));
		_statFinder.put("Reflex", new Stat("Reflex","",StatType.defense));
		_statFinder.put("Will", new Stat("Will","",StatType.defense));
		_statFinder.put("Level", new Stat("Level","",StatType.detail));
		
		_statFinder.put("Initiative", new Stat("Initiative","",StatType.other));
		_statFinder.put("Speed", new Stat("Speed","",StatType.other));
		_statFinder.put("Power Points", new Stat("Power Points","",StatType.other));
		_statFinder.put("Passive Perception", new Stat("Passive Perception","",StatType.other));
		_statFinder.put("Passive Insight", new Stat("Passive Insight","",StatType.other));
		
		_statFinder.put("Strength", new Stat("Strength","",StatType.ability));
		_statFinder.put("Constitution", new Stat("Constitution","",StatType.ability));
		_statFinder.put("Dexterity", new Stat("Dexterity","",StatType.ability));
		_statFinder.put("Intelligence", new Stat("Intelligence","",StatType.ability));
		_statFinder.put("Wisdom", new Stat("Wisdom","",StatType.ability));
		_statFinder.put("Charisma", new Stat("Charisma","",StatType.ability));

		_statFinder.put("Hit Points", new Stat("Hit Points","",StatType.health));
		_statFinder.put("Healing Surges", new Stat("Healing Surges","",StatType.health));
		
		_statFinder.put("Acrobatics", new Stat("Acrobatics", "", StatType.skill));
		_statFinder.put("Athletics", new Stat("Athletics", "", StatType.skill));
		_statFinder.put("Arcana", new Stat("Arcana", "", StatType.skill));
		_statFinder.put("Bluff", new Stat("Bluff", "", StatType.skill));
		_statFinder.put("Diplomacy", new Stat("Diplomacy", "", StatType.skill));
		_statFinder.put("Dungeoneering", new Stat("Dungeoneering", "", StatType.skill));
		_statFinder.put("Endurance", new Stat("Endurance", "", StatType.skill));
		_statFinder.put("Heal", new Stat("Heal", "", StatType.skill));
		_statFinder.put("History", new Stat("History", "", StatType.skill));
		_statFinder.put("Insight", new Stat("Insight", "", StatType.skill));
		_statFinder.put("Intimidate", new Stat("Intimidate", "", StatType.skill));
		_statFinder.put("Nature", new Stat("Nature", "", StatType.skill));
		_statFinder.put("Perception", new Stat("Perception", "", StatType.skill));
		_statFinder.put("Religion", new Stat("Religion", "", StatType.skill));
		_statFinder.put("Stealth", new Stat("Stealth", "", StatType.skill));
		_statFinder.put("Streetwise", new Stat("Streetwise", "", StatType.skill));
		_statFinder.put("Thievery", new Stat("Thievery", "", StatType.skill));
		
		
	}
	
	@Override
	public void characters(char[] ch, int start, int length)
			throws SAXException {
		super.characters(ch, start, length);
		
		String value = new String(ch, start, length).trim();
		String currentTag = _tagPath.peek();
		
		switch(_step){
		case details:
			if(_statFinder.containsKey(currentTag)){
				Stat s = _statFinder.get(currentTag);
				s.Value = value;
				_db.addStat(_characterID, s);
			}
			break;
		case statblock:
			//nothing to do
			break;
		case powers:
			if(_currentPower != null){
				if(!_powerHaveWeaponData){
					if(currentTag.equals("AttackBonus")) _currentPower.AttackBonus = value;
					if(currentTag.equals("Defense")) _currentPower.Defense = value;
					if(currentTag.equals("Damage")) _currentPower.Damage = value;
				}
				
				if(currentTag.equals("specific")){
					switch(_powerAttribute){
					case Action:
						_currentPower.ActionType = value;
						break;
					case Usage:
						_currentPower.Usage = value;
						break;
					case NotLooking:
						break;
					}
				}
			}
			break;
		case end:
			
		}
	}
	
	@Override
	public void endElement(String uri, String localName, String qName)
			throws SAXException {
		super.endElement(uri, localName, qName);
		
		switch(_step){
		case details:
			if(localName.contains("Details"))
				_step = MySteps.statblock;
			
			break;
		case statblock:
			if(localName.contains("StatBlock")) 
				_step = MySteps.ruleselements;
				
			break;
		case ruleselements:
			if(localName.contains("RulesElementTally")) 
				_step = MySteps.powers;
			
			break;
		case powers:
			if(localName.contains("PowerStats")) 
				_step = MySteps.end;
			
			if(localName.contains("Weapon"))
				_powerHaveWeaponData = true;
			
			if(_currentPower != null && localName.contains("Power")){
				_db.addPower(_characterID, _currentPower);
				_currentPower = null;
			}
				
			break;
		case end:
			
		}
		_tagPath.pop();
	}

	@Override
	public void startElement(String uri, String localName, String qName,
			Attributes attributes) throws SAXException {
		super.startElement(uri, localName, qName, attributes);
		
		switch(_step){
		case start:
			//start here
			if(localName.contains("Details")) 
				_step = MySteps.details;
		case details:
			//start 
			break;
		case statblock:

			if(localName.equals("Stat")){
				String value = attributes.getValue("value");
				_currentStat = new Stat(null, value, null);
			}
			if(localName.contains("alias")){
				String name = attributes.getValue("name");
				if(_statFinder.containsKey(name)){
					Stat s = _statFinder.get(name);
					s.Value = _currentStat.Value;
					_db.addStat(_characterID, s);
					_currentStat = null;
				}
			}
			break;
		case ruleselements:
		{
			if(localName.equals("RulesElement")){
				String type = attributes.getValue("type");
				String name = attributes.getValue("name");
				name = name.replace("&apos;", "'");
				if(type.equals("Class")){
					_db.addStat(_characterID, new Stat("Class",name,StatType.detail));
				}else if(type.equals("Feat")){
					_db.addStat(_characterID, new Stat(name,"",StatType.feat));
				}else if(type.equals("Racial Trait")){
					_db.addStat(_characterID, new Stat(name,"",StatType.racialtrait));
				}else if(type.equals("Class Feature")){
					_db.addStat(_characterID, new Stat(name,"",StatType.classfeature));
				}else if(type.equals("Power")){
					//Log.i("kerpow","power found " + name + " @ " + attributes.getValue("url"));
					_db.addStat(_characterID, new Stat(name,attributes.getValue("url"),StatType.powerurl));
				}
			}
		}
			break;
		case powers:
			if(localName.equals("Power")){								
				_currentPower = new Power();
				_powerHaveWeaponData = false;
				_powerAttribute = MyPowerSpecifics.NotLooking;
				_currentPower.Name = attributes.getValue("name").trim();
				//added correctly
			}
			if(localName.equals("specific")){
				String name = attributes.getValue("name");
				if(name.equals("Power Usage")) _powerAttribute = MyPowerSpecifics.Usage;
				
				if(name.equals("Action Type")) {
					
					_powerAttribute = MyPowerSpecifics.Action;
				}
					
			}
			break;
		}

		_tagPath.push(localName);
	}

	private class MyFileReader extends FileReader{

		public MyFileReader(File fr) throws FileNotFoundException {
			super(fr);
		}

		@Override
		public int read() throws IOException {
			
			this.doProgress(1);
			
			return super.read();
		}

		@Override
		public int read(char[] buf, int offset, int length) throws IOException {
			this.doProgress(length);
			return super.read(buf, offset, length);
		}
		
		private void doProgress(int length){
			_currentBytes += length;
			int percent = (int) (_currentBytes / _totalBytes*100);
			if(_OnProgressPublishHandler != null && percent != _lastPublishedPercent ) _OnProgressPublishHandler.onProgressPublish(percent);
			_lastPublishedPercent = percent;
		}
	}

}
